/**
 * File: SplitArrayStore.java
 * Created by: mhaimel
 * Created on: 24 Aug 2009
 * CVS:  $Id: SplitArrayStore.java,v 1.1 2009/10/09 15:18:24 mhaimel Exp $
 */
package uk.ac.ebi.curtain.service.impl;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicReferenceArray;

import org.apache.commons.lang.NotImplementedException;

import uk.ac.ebi.curtain.service.IdentifierStore;
import uk.ac.ebi.curtain.utils.CurtainUncheckedException;

/**
 * @author mhaimel
 *
 */
public class SplitArrayStore <O> implements IdentifierStore<Integer, O> {
	public static final int SPLIT_SIZE = 100000;
	private final int splitSize;
	private final AtomicInteger currSize = new AtomicInteger(0);
	private final AtomicReference<AtomicReferenceArray<AtomicReferenceArray<O>>> arr = new AtomicReference<AtomicReferenceArray<AtomicReferenceArray<O>>>();
	
	public static void main(String[] args) {
		SplitArrayStore<String> store = new SplitArrayStore<String>(10);
		store.reset(138);
		store.registerIdentifier(0, "0");
		store.registerIdentifier(9, "9");
		store.registerIdentifier(10, "10");
		store.registerIdentifier(137, "137");
		System.out.println(store.getIdentifier(0));
		System.out.println(store.getIdentifier(9));
		System.out.println(store.getIdentifier(10));
		System.out.println(store.getIdentifier(137));		
	}
	
	
	public SplitArrayStore() {
		this(SPLIT_SIZE);
	}
	public SplitArrayStore(int splitSize) {
		this.splitSize = splitSize;
		this.arr.set(new AtomicReferenceArray<AtomicReferenceArray<O>>(currSize.get()));
	}
	
	private int getSplitSize() {
		return splitSize;
	}
	
	@Override
	public void clear() {
		this.arr.set(new AtomicReferenceArray<AtomicReferenceArray<O>>(0));
	}
	@Override
	public O getIdentifier(Integer input) {
		return get(getId1(input),getId2(input));
	}
	
	private int getId1(Integer id){
		return id/getSplitSize();
	}
	private int getId2(Integer id){
		return id%getSplitSize();
	}
	
	private O get(int arr1, int arr2){
		return arr.get().get(arr1).get(arr2);
	}
	private O add(int arr1, int arr2, O obj){
		return arr.get().get(arr1).getAndSet(arr2,obj);
	}
	
	@Override
	public Iterable<Integer> getKeys() {
		throw new NotImplementedException("Not supported!!!");
	}
	@Override
	public O getOrAddIdentifier(Integer input) {
		if(hasIdentifier(input)){
			return getIdentifier(input);
		} else {
			throw new CurtainUncheckedException(
					"Identifier "+input+" out of range from " +  this.getSize() +"! Automatic adding not supported");
		}
	}
	
	@Override
	public Integer getSize() {
		return currSize.get();
	}
	
	@Override
	public boolean hasIdentifier(Integer input) {
		return input >= 0 && input < getSize();
	}
	
	@Override
	public void registerIdentifier(Integer input, O output) {
		add(getId1(input),getId2(input),output);	
	}
	
	@Override
	public void reset(Integer size) {
		int arr1Len = getId1(size)+1;
		AtomicReferenceArray<AtomicReferenceArray<O>> nVal = new AtomicReferenceArray<AtomicReferenceArray<O>>(arr1Len);
		for(int i = 0; i < arr1Len; ++i ){
			AtomicReferenceArray<O> subArr = new AtomicReferenceArray<O>(getSplitSize());
			nVal.set(i, subArr);
		}
		arr.set(nVal);
		currSize.set(size);
	}
}
